/*
 * @author Chris
 * @since 2023/1/21 20:07
 *
 * @brief    The project will control the step motor through key matrix.
 *
 * @note     {@link StartMotor()} This function will start motor.
 *           {@link StopMotor()} This function will stop motor.
 *           {@link KeyAction()} This function will dispatch the action of key.
 *           {@link KeyDriver()} key driver function.
 *           {@link KeyScan()} key scan function.
 *           {@link TurnMotor()} motor run function.
 *
 * @warning  Do not to ...
 *
 * Change Logs:
 *   Date           Author       Notes
 *   2023-01-21     Chris        the first version
 */

#include "reg51.h"

#define KEY_IN_1 P2_4
#define KEY_IN_2 P2_5
#define KEY_IN_3 P2_6
#define KEY_IN_4 P2_7

// 按键矩阵编号与标准键盘映射
unsigned char __code keyboardMap[4][4] = {
        {0x31, 0x32, 0x33, 0x26},   // 1、2、3、上
        {0x34, 0x35, 0x36, 0x25},   // 4、5、6、左
        {0x37, 0x38, 0x39, 0x28},   // 7、8、9、下
        {0x30, 0x1B, 0x0D, 0x27},   // 0、ESC、ENTER、右
};

unsigned char keyState[4][4] = {
        {1, 1, 1, 1},
        {1, 1, 1, 1},
        {1, 1, 1, 1},
        {1, 1, 1, 1},
};


signed long beats = 0;

void KeyDriver();

int main() {
    EA = 1;      // 使能总中断
    TMOD = 0x01; // 设置 T0 为模式 1
    TH0 = 0xFC;  // 为 T0 赋初值，定时 1ms
    TL0 = 0x66;

    ET0 = 1;     // 使能 T0 中断
    TR0 = 1;     // 启动 T0

    while (1) {
        KeyDriver();
    }
}

void StartMotor(signed long angle) {
    EA = 0;
    beats = angle * 4096 / 360;
    EA = 1;
}

void StopMotor() {
    EA = 0;
    beats = 0;
    EA = 1;
}

void KeyAction(unsigned char key) {
    static signed char direction = 1;
    if (key >= 0x30 && key <= 0x39) {
        StartMotor(direction * 360 * (key - 0x30));
    } else if (key == 0x26) {
        direction = 1;
    } else if (key == 0x28) {
        direction = -1;
    } else if (key == 0x25) {
        StartMotor(90);
    } else if (key == 0x27) {
        StartMotor(-90);
    } else if (key == 0x1B) {
        StopMotor();
    }
}

void KeyDriver() {
    static unsigned char keyBackup[4][4] = {
            {1, 1, 1, 1},
            {1, 1, 1, 1},
            {1, 1, 1, 1},
            {1, 1, 1, 1},
    };

    for (int i = 0; i < 4; ++i) {
        for (int j = 0; j < 4; ++j) {
            if (keyState[i][j] != keyBackup[i][j]) {
                if (keyBackup[i][j] == 1) {
                    KeyAction(keyboardMap[i][j]);
                }
                keyBackup[i][j] = keyState[i][j];
            }
        }
    }

}

void KeyScan() {
    static unsigned char row = 0;
    static unsigned char keyBuf[4][4] = {
            {0xFF, 0xFF, 0xFF, 0xFF},
            {0xFF, 0xFF, 0xFF, 0xFF},
            {0xFF, 0xFF, 0xFF, 0xFF},
            {0xFF, 0xFF, 0xFF, 0xFF},
    };

    keyBuf[row][0] = (keyBuf[row][0] << 1) | KEY_IN_1;
    keyBuf[row][1] = (keyBuf[row][1] << 1) | KEY_IN_2;
    keyBuf[row][2] = (keyBuf[row][2] << 1) | KEY_IN_3;
    keyBuf[row][3] = (keyBuf[row][3] << 1) | KEY_IN_4;

    for (int i = 0; i < 4; ++i) {
        if ((keyBuf[row][i] & 0x0F) == 0x0F) {
            keyState[row][i] = 1;
        } else if ((keyBuf[row][i] & 0x0F) == 0x00) {
            keyState[row][i] = 0;
        } else {}
    }

    row++;
    row = row & 0x03;

    P2 = (P2 & 0xF0) | (~(1 << (3 - row)));
}

void TurnMotor() {
    static unsigned char index = 0;
    // 步进电机八节拍模式真值表
    unsigned char __code stepperMotorTruthTable[8] = {
            0xE, 0xC, 0xD, 0x9, 0xB, 0x3, 0x7, 0x6
    };


    if (beats > 0) {
        beats--;
        P1 = P1 & 0xF0 | stepperMotorTruthTable[index];
        index++;
    } else if (beats < 0) {
        beats++;
        P1 = P1 & 0xF0 | stepperMotorTruthTable[index];
        index--;
    } else {
        P1 = P1 | 0x0F;
    }

    index = index & 0x07;
}

/**
 * 定时器 T0 中断服务函数
 */
void InterruptTimer0() __interrupt(1) {
    static unsigned char div = 0;
    TH0 = 0xFC; // 为 T0 赋初值，定时 1ms
    TL0 = 0x66;

    KeyScan();  // 扫描键盘

    div++;
    div = div & 0x03;   // 实测中，2ms 一节拍无法顺利转动
    if (div == 0) {
        TurnMotor();    // 电机运行
    }
}

